/*****************************************************************************
 * Copyright (C) The Apache Software Foundation. All rights reserved.        *
 * ------------------------------------------------------------------------- *
 * This software is published under the terms of the Apache Software License *
 * version 1.1, a copy of which has been included with this distribution in  *
 * the LICENSE file.                                                         *
 *****************************************************************************/

package com.kitfox.svg.batik;

import java.awt.Color;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.lang.ref.WeakReference;

//import org.apache.batik.ext.awt.image.GraphicsUtil;

/**
 * This is the superclass for all PaintContexts which use a multiple color
 * gradient to fill in their raster. It provides the actual color interpolation
 * functionality. Subclasses only have to deal with using the gradient to fill
 * pixels in a raster.
 *
 * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
 * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
 * @version $Id: MultipleGradientPaintContext.java,v 1.1 2004/09/06 19:35:39
 *          kitfox Exp $
 *
 */
abstract class MultipleGradientPaintContext implements PaintContext {

	protected final static boolean DEBUG = false;

	/**
	 * The color model data is generated in (always un premult).
	 */
	protected ColorModel dataModel;
	/**
	 * PaintContext's output ColorModel ARGB if colors are not all opaque, RGB
	 * otherwise. Linear and premult are matched to output ColorModel.
	 */
	protected ColorModel model;

	/** Color model used if gradient colors are all opaque */
	private static ColorModel lrgbmodel_NA = new DirectColorModel(
			ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 24, 0xff0000,
			0xFF00, 0xFF, 0x0, false, DataBuffer.TYPE_INT);

	private static ColorModel srgbmodel_NA = new DirectColorModel(
			ColorSpace.getInstance(ColorSpace.CS_sRGB), 24, 0xff0000, 0xFF00,
			0xFF, 0x0, false, DataBuffer.TYPE_INT);

	/** Color model used if some gradient colors are transparent */
	private static ColorModel lrgbmodel_A = new DirectColorModel(
			ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB), 32, 0xff0000,
			0xFF00, 0xFF, 0xFF000000, false, DataBuffer.TYPE_INT);

	private static ColorModel srgbmodel_A = new DirectColorModel(
			ColorSpace.getInstance(ColorSpace.CS_sRGB), 32, 0xff0000, 0xFF00,
			0xFF, 0xFF000000, false, DataBuffer.TYPE_INT);

	/** The cached colorModel */
	protected static ColorModel cachedModel;

	/** The cached raster, which is reusable among instances */
	protected static WeakReference cached;

	/** Raster is reused whenever possible */
	protected WritableRaster saved;

	/** The method to use when painting out of the gradient bounds. */
	protected MultipleGradientPaint.CycleMethodEnum cycleMethod;

	/** The colorSpace in which to perform the interpolation */
	protected MultipleGradientPaint.ColorSpaceEnum colorSpace;

	/** Elements of the inverse transform matrix. */
	protected float a00, a01, a10, a11, a02, a12;

	/**
	 * This boolean specifies whether we are in simple lookup mode, where an
	 * input value between 0 and 1 may be used to directly index into a single
	 * array of gradient colors. If this boolean value is false, then we have to
	 * use a 2-step process where we have to determine which gradient array we
	 * fall into, then determine the index into that array.
	 */
	protected boolean isSimpleLookup = true;

	/**
	 * This boolean indicates if the gradient appears to have sudden
	 * discontinuities in it, this may be because of multiple stops at the same
	 * location or use of the REPEATE mode.
	 */
	protected boolean hasDiscontinuity = false;

	/**
	 * Size of gradients array for scaling the 0-1 index when looking up colors
	 * the fast way.
	 */
	protected int fastGradientArraySize;

	/**
	 * Array which contains the interpolated color values for each interval,
	 * used by calculateSingleArrayGradient(). It is protected for possible
	 * direct access by subclasses.
	 */
	protected int[] gradient;

	/**
	 * Array of gradient arrays, one array for each interval. Used by
	 * calculateMultipleArrayGradient().
	 */
	protected int[][] gradients;

	/**
	 * This holds the blend of all colors in the gradient. we use this at
	 * extreamly low resolutions to ensure we get a decent blend of the colors.
	 */
	protected int gradientAverage;

	/**
	 * This holds the color to use when we are off the bottom of the gradient
	 */
	protected int gradientUnderflow;

	/**
	 * This holds the color to use when we are off the top of the gradient
	 */
	protected int gradientOverflow;

	/** Length of the 2D slow lookup gradients array. */
	protected int gradientsLength;

	/** Normalized intervals array */
	protected float[] normalizedIntervals;

	/** fractions array */
	protected float[] fractions;

	/** Used to determine if gradient colors are all opaque */
	private int transparencyTest;

	/** Colorspace conversion lookup tables */
	private static final int SRGBtoLinearRGB[] = new int[256];
	private static final int LinearRGBtoSRGB[] = new int[256];

	// build the tables
	static {
		for (int k = 0; k < 256; k++) {
			SRGBtoLinearRGB[k] = convertSRGBtoLinearRGB(k);
			LinearRGBtoSRGB[k] = convertLinearRGBtoSRGB(k);
		}
	}

	/**
	 * Constant number of max colors between any 2 arbitrary colors. Used for
	 * creating and indexing gradients arrays.
	 */
	protected static final int GRADIENT_SIZE = 256;
	protected static final int GRADIENT_SIZE_INDEX = GRADIENT_SIZE - 1;

	/**
	 * Maximum length of the fast single-array. If the estimated array size is
	 * greater than this, switch over to the slow lookup method. No particular
	 * reason for choosing this number, but it seems to provide satisfactory
	 * performance for the common case (fast lookup).
	 */
	private static final int MAX_GRADIENT_ARRAY_SIZE = 5000;

	/**
	 * Constructor for superclass. Does some initialization, but leaves most of
	 * the heavy-duty math for calculateGradient(), so the subclass may do some
	 * other manipulation beforehand if necessary. This is not possible if this
	 * computation is done in the superclass constructor which always gets
	 * called first.
	 **/
	public MultipleGradientPaintContext(ColorModel cm, Rectangle deviceBounds,
			Rectangle2D userBounds, AffineTransform t, RenderingHints hints,
			float[] fractions, Color[] colors,
			MultipleGradientPaint.CycleMethodEnum cycleMethod,
			MultipleGradientPaint.ColorSpaceEnum colorSpace)
			throws NoninvertibleTransformException {
		// We have to deal with the cases where the 1st gradient stop is not
		// equal to 0 and/or the last gradient stop is not equal to 1.
		// In both cases, create a new point and replicate the previous
		// extreme point's color.

		boolean fixFirst = false;
		boolean fixLast = false;
		int len = fractions.length;

		// if the first gradient stop is not equal to zero, fix this condition
		if (fractions[0] != 0f) {
			fixFirst = true;
			len++;
		}

		// if the last gradient stop is not equal to one, fix this condition
		if (fractions[fractions.length - 1] != 1f) {
			fixLast = true;
			len++;
		}

		for (int i = 0; i < fractions.length - 1; i++) {
			if (fractions[i] == fractions[i + 1]) {
				len--;
			}
		}

		this.fractions = new float[len];
		Color[] loColors = new Color[len - 1];
		Color[] hiColors = new Color[len - 1];
		normalizedIntervals = new float[len - 1];

		gradientUnderflow = colors[0].getRGB();
		gradientOverflow = colors[colors.length - 1].getRGB();

		int idx = 0;
		if (fixFirst) {
			this.fractions[0] = 0;
			loColors[0] = colors[0];
			hiColors[0] = colors[0];
			normalizedIntervals[0] = fractions[0];
			idx++;
		}

		for (int i = 0; i < fractions.length - 1; i++) {
			if (fractions[i] == fractions[i + 1]) {
				// System.out.println("EQ Fracts");
				if (!colors[i].equals(colors[i + 1])) {
					hasDiscontinuity = true;
				}
				continue;
			}
			this.fractions[idx] = fractions[i];
			loColors[idx] = colors[i];
			hiColors[idx] = colors[i + 1];
			normalizedIntervals[idx] = fractions[i + 1] - fractions[i];
			idx++;
		}

		this.fractions[idx] = fractions[fractions.length - 1];

		if (fixLast) {
			loColors[idx] = hiColors[idx] = colors[colors.length - 1];
			normalizedIntervals[idx] = 1 - fractions[fractions.length - 1];
			idx++;
			this.fractions[idx] = 1;
		}

		// The inverse transform is needed to from device to user space.
		// Get all the components of the inverse transform matrix.
		AffineTransform tInv = t.createInverse();

		double m[] = new double[6];
		tInv.getMatrix(m);
		a00 = (float) m[0];
		a10 = (float) m[1];
		a01 = (float) m[2];
		a11 = (float) m[3];
		a02 = (float) m[4];
		a12 = (float) m[5];

		// copy some flags
		this.cycleMethod = cycleMethod;
		this.colorSpace = colorSpace;

		// Setup an example Model, we may refine it later.
		if (cm.getColorSpace() == lrgbmodel_A.getColorSpace()) {
			dataModel = lrgbmodel_A;
		} else if (cm.getColorSpace() == srgbmodel_A.getColorSpace()) {
			dataModel = srgbmodel_A;
		} else {
			throw new IllegalArgumentException(
					"Unsupported ColorSpace for interpolation");
		}

		calculateGradientFractions(loColors, hiColors);

		model = GraphicsUtil.coerceColorModel(dataModel,
				cm.isAlphaPremultiplied());
	}

	/**
	 * This function is the meat of this class. It calculates an array of
	 * gradient colors based on an array of fractions and color values at those
	 * fractions.
	 */
	protected final void calculateGradientFractions(Color[] loColors,
			Color[] hiColors) {

		// if interpolation should occur in Linear RGB space, convert the
		// colors using the lookup table
		if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
			for (int i = 0; i < loColors.length; i++) {
				loColors[i] = new Color(SRGBtoLinearRGB[loColors[i].getRed()],
						SRGBtoLinearRGB[loColors[i].getGreen()],
						SRGBtoLinearRGB[loColors[i].getBlue()],
						loColors[i].getAlpha());

				hiColors[i] = new Color(SRGBtoLinearRGB[hiColors[i].getRed()],
						SRGBtoLinearRGB[hiColors[i].getGreen()],
						SRGBtoLinearRGB[hiColors[i].getBlue()],
						hiColors[i].getAlpha());
			}
		}

		// initialize to be fully opaque for ANDing with colors
		transparencyTest = 0xff000000;

		// array of interpolation arrays
		gradients = new int[fractions.length - 1][];
		gradientsLength = gradients.length;

		// Find smallest interval
		int n = normalizedIntervals.length;

		float Imin = 1;

		for (int i = 0; i < n; i++) {
			Imin = (Imin > normalizedIntervals[i]) ? normalizedIntervals[i]
					: Imin;
		}

		// estimate the size of the entire gradients array.
		// This is to prevent a tiny interval from causing the size of array to
		// explode. If the estimated size is too large, break to using
		// seperate arrays for each interval, and using an indexing scheme at
		// look-up time.
		int estimatedSize = 0;

		if (Imin == 0) {
			estimatedSize = Integer.MAX_VALUE;
			hasDiscontinuity = true;
		} else {
			for (int i = 0; i < normalizedIntervals.length; i++) {
				estimatedSize += (normalizedIntervals[i] / Imin)
						* GRADIENT_SIZE;
			}
		}

		if (estimatedSize > MAX_GRADIENT_ARRAY_SIZE) {
			// slow method
			calculateMultipleArrayGradient(loColors, hiColors);
			if ((cycleMethod == MultipleGradientPaint.REPEAT)
					&& (gradients[0][0] != gradients[gradients.length
							- 1][GRADIENT_SIZE_INDEX])) {
				hasDiscontinuity = true;
			}
		} else {
			// fast method
			calculateSingleArrayGradient(loColors, hiColors, Imin);
			if ((cycleMethod == MultipleGradientPaint.REPEAT)
					&& (gradient[0] != gradient[fastGradientArraySize])) {
				hasDiscontinuity = true;
			}
		}

		// Use the most 'economical' model (no alpha).
		if ((transparencyTest >>> 24) == 0xff) {
			if (dataModel.getColorSpace() == lrgbmodel_NA.getColorSpace()) {
				dataModel = lrgbmodel_NA;
			} else if (dataModel.getColorSpace() == srgbmodel_NA.getColorSpace()) {
				dataModel = srgbmodel_NA;
			}
			model = dataModel;
		}
	}

	/**
	 * FAST LOOKUP METHOD
	 *
	 * This method calculates the gradient color values and places them in a
	 * single int array, gradient[]. It does this by allocating space for each
	 * interval based on its size relative to the smallest interval in the
	 * array. The smallest interval is allocated 255 interpolated values (the
	 * maximum number of unique in-between colors in a 24 bit color system), and
	 * all other intervals are allocated size = (255 * the ratio of their size
	 * to the smallest interval).
	 *
	 * This scheme expedites a speedy retrieval because the colors are
	 * distributed along the array according to their user-specified
	 * distribution. All that is needed is a relative index from 0 to 1.
	 *
	 * The only problem with this method is that the possibility exists for the
	 * array size to balloon in the case where there is a disproportionately
	 * small gradient interval. In this case the other intervals will be
	 * allocated huge space, but much of that data is redundant. We thus need to
	 * use the space conserving scheme below.
	 *
	 * @param Imin
	 *            the size of the smallest interval
	 *
	 */
	private void calculateSingleArrayGradient(Color[] loColors,
			Color[] hiColors, float Imin) {

		// set the flag so we know later it is a non-simple lookup
		isSimpleLookup = true;

		int rgb1; // 2 colors to interpolate
		int rgb2;

		int gradientsTot = 1; // the eventual size of the single array

		// These are fixed point 8.16 (start with 0.5)
		int aveA = 0x008000;
		int aveR = 0x008000;
		int aveG = 0x008000;
		int aveB = 0x008000;

		// for every interval (transition between 2 colors)
		for (int i = 0; i < gradients.length; i++) {

			// create an array whose size is based on the ratio to the
			// smallest interval.
			int nGradients = (int) ((normalizedIntervals[i] / Imin) * 255f);
			gradientsTot += nGradients;
			gradients[i] = new int[nGradients];

			// the the 2 colors (keyframes) to interpolate between
			rgb1 = loColors[i].getRGB();
			rgb2 = hiColors[i].getRGB();

			// fill this array with the colors in between rgb1 and rgb2
			interpolate(rgb1, rgb2, gradients[i]);

			// Calculate Average of two colors...
			int argb = gradients[i][GRADIENT_SIZE / 2];
			float norm = normalizedIntervals[i];
			aveA += (int) (((argb >> 8) & 0xFF0000) * norm);
			aveR += (int) (((argb) & 0xFF0000) * norm);
			aveG += (int) (((argb << 8) & 0xFF0000) * norm);
			aveB += (int) (((argb << 16) & 0xFF0000) * norm);

			// if the colors are opaque, transparency should still be 0xff000000
			transparencyTest &= rgb1;
			transparencyTest &= rgb2;
		}

		gradientAverage = (((aveA & 0xFF0000) << 8) | ((aveR & 0xFF0000))
				| ((aveG & 0xFF0000) >> 8) | ((aveB & 0xFF0000) >> 16));

		// Put all gradients in a single array
		gradient = new int[gradientsTot];
		int curOffset = 0;
		for (int i = 0; i < gradients.length; i++) {
			System.arraycopy(gradients[i], 0, gradient, curOffset,
					gradients[i].length);
			curOffset += gradients[i].length;
		}
		gradient[gradient.length - 1] = hiColors[hiColors.length - 1].getRGB();

		// if interpolation occurred in Linear RGB space, convert the
		// gradients back to SRGB using the lookup table
		if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
			if (dataModel.getColorSpace() == ColorSpace
					.getInstance(ColorSpace.CS_sRGB)) {
				for (int i = 0; i < gradient.length; i++) {
					gradient[i] = convertEntireColorLinearRGBtoSRGB(
							gradient[i]);
				}
				gradientAverage = convertEntireColorLinearRGBtoSRGB(
						gradientAverage);
			}
		} else {
			if (dataModel.getColorSpace() == ColorSpace
					.getInstance(ColorSpace.CS_LINEAR_RGB)) {
				for (int i = 0; i < gradient.length; i++) {
					gradient[i] = convertEntireColorSRGBtoLinearRGB(
							gradient[i]);
				}
				gradientAverage = convertEntireColorSRGBtoLinearRGB(
						gradientAverage);
			}
		}

		fastGradientArraySize = gradient.length - 1;
	}

	/**
	 * SLOW LOOKUP METHOD
	 *
	 * This method calculates the gradient color values for each interval and
	 * places each into its own 255 size array. The arrays are stored in
	 * gradients[][]. (255 is used because this is the maximum number of unique
	 * colors between 2 arbitrary colors in a 24 bit color system)
	 *
	 * This method uses the minimum amount of space (only 255 * number of
	 * intervals), but it aggravates the lookup procedure, because now we have
	 * to find out which interval to select, then calculate the index within
	 * that interval. This causes a significant performance hit, because it
	 * requires this calculation be done for every point in the rendering loop.
	 *
	 * For those of you who are interested, this is a classic example of the
	 * time-space tradeoff.
	 *
	 */
	private void calculateMultipleArrayGradient(Color[] loColors,
			Color[] hiColors) {

		// set the flag so we know later it is a non-simple lookup
		isSimpleLookup = false;

		int rgb1; // 2 colors to interpolate
		int rgb2;

		// These are fixed point 8.16 (start with 0.5)
		int aveA = 0x008000;
		int aveR = 0x008000;
		int aveG = 0x008000;
		int aveB = 0x008000;

		// for every interval (transition between 2 colors)
		for (int i = 0; i < gradients.length; i++) {

			// This interval will never actually be used (zero size)
			if (normalizedIntervals[i] == 0) {
				continue;
			}

			// create an array of the maximum theoretical size for each interval
			gradients[i] = new int[GRADIENT_SIZE];

			// get the the 2 colors
			rgb1 = loColors[i].getRGB();
			rgb2 = hiColors[i].getRGB();

			// fill this array with the colors in between rgb1 and rgb2
			interpolate(rgb1, rgb2, gradients[i]);

			// Calculate Average of two colors...
			int argb = gradients[i][GRADIENT_SIZE / 2];
			float norm = normalizedIntervals[i];
			aveA += (int) (((argb >> 8) & 0xFF0000) * norm);
			aveR += (int) (((argb) & 0xFF0000) * norm);
			aveG += (int) (((argb << 8) & 0xFF0000) * norm);
			aveB += (int) (((argb << 16) & 0xFF0000) * norm);

			// if the colors are opaque, transparency should still be 0xff000000
			transparencyTest &= rgb1;
			transparencyTest &= rgb2;
		}

		gradientAverage = (((aveA & 0xFF0000) << 8) | ((aveR & 0xFF0000))
				| ((aveG & 0xFF0000) >> 8) | ((aveB & 0xFF0000) >> 16));

		// if interpolation occurred in Linear RGB space, convert the
		// gradients back to SRGB using the lookup table
		if (colorSpace == LinearGradientPaint.LINEAR_RGB) {
			if (dataModel.getColorSpace() == ColorSpace
					.getInstance(ColorSpace.CS_sRGB)) {
				for (int j = 0; j < gradients.length; j++) {
					for (int i = 0; i < gradients[j].length; i++) {
						gradients[j][i] = convertEntireColorLinearRGBtoSRGB(
								gradients[j][i]);
					}
				}
				gradientAverage = convertEntireColorLinearRGBtoSRGB(
						gradientAverage);
			}
		} else {
			if (dataModel.getColorSpace() == ColorSpace
					.getInstance(ColorSpace.CS_LINEAR_RGB)) {
				for (int j = 0; j < gradients.length; j++) {
					for (int i = 0; i < gradients[j].length; i++) {
						gradients[j][i] = convertEntireColorSRGBtoLinearRGB(
								gradients[j][i]);
					}
				}
				gradientAverage = convertEntireColorSRGBtoLinearRGB(
						gradientAverage);
			}
		}
	}

	/**
	 * Yet another helper function. This one linearly interpolates between 2
	 * colors, filling up the output array.
	 *
	 * @param rgb1
	 *            the start color
	 * @param rgb2
	 *            the end color
	 * @param output
	 *            the output array of colors... assuming this is not null.
	 *
	 */
	private static void interpolate(int rgb1, int rgb2, int[] output) {

		int a1, r1, g1, b1, da, dr, dg, db; // color components

		// step between interpolated values.
		float stepSize = 1 / (float) output.length;

		// extract color components from packed integer
		a1 = (rgb1 >> 24) & 0xff;
		r1 = (rgb1 >> 16) & 0xff;
		g1 = (rgb1 >> 8) & 0xff;
		b1 = (rgb1) & 0xff;
		// calculate the total change in alpha, red, green, blue
		da = ((rgb2 >> 24) & 0xff) - a1;
		dr = ((rgb2 >> 16) & 0xff) - r1;
		dg = ((rgb2 >> 8) & 0xff) - g1;
		db = ((rgb2) & 0xff) - b1;

		// for each step in the interval calculate the in-between color by
		// multiplying the normalized current position by the total color change
		// (.5 is added to prevent truncation round-off error)
		for (int i = 0; i < output.length; i++) {
			output[i] = (((int) ((a1 + i * da * stepSize) + .5) << 24))
					| (((int) ((r1 + i * dr * stepSize) + .5) << 16))
					| (((int) ((g1 + i * dg * stepSize) + .5) << 8))
					| (((int) ((b1 + i * db * stepSize) + .5)));
		}
	}

	/**
	 * Yet another helper function. This one extracts the color components of an
	 * integer RGB triple, converts them from LinearRGB to SRGB, then recompacts
	 * them into an int.
	 */
	private static int convertEntireColorLinearRGBtoSRGB(int rgb) {

		int a1, r1, g1, b1; // color components

		// extract red, green, blue components
		a1 = (rgb >> 24) & 0xff;
		r1 = (rgb >> 16) & 0xff;
		g1 = (rgb >> 8) & 0xff;
		b1 = rgb & 0xff;

		// use the lookup table
		r1 = LinearRGBtoSRGB[r1];
		g1 = LinearRGBtoSRGB[g1];
		b1 = LinearRGBtoSRGB[b1];

		// re-compact the components
		return ((a1 << 24) | (r1 << 16) | (g1 << 8) | b1);
	}

	/**
	 * Yet another helper function. This one extracts the color components of an
	 * integer RGB triple, converts them from LinearRGB to SRGB, then recompacts
	 * them into an int.
	 */
	private static int convertEntireColorSRGBtoLinearRGB(int rgb) {

		int a1, r1, g1, b1; // color components

		// extract red, green, blue components
		a1 = (rgb >> 24) & 0xff;
		r1 = (rgb >> 16) & 0xff;
		g1 = (rgb >> 8) & 0xff;
		b1 = rgb & 0xff;

		// use the lookup table
		r1 = SRGBtoLinearRGB[r1];
		g1 = SRGBtoLinearRGB[g1];
		b1 = SRGBtoLinearRGB[b1];

		// re-compact the components
		return ((a1 << 24) | (r1 << 16) | (g1 << 8) | b1);
	}

	/**
	 * Helper function to index into the gradients array. This is necessary
	 * because each interval has an array of colors with uniform size 255.
	 * However, the color intervals are not necessarily of uniform length, so a
	 * conversion is required.
	 *
	 * @param position
	 *            the unmanipulated position. want to map this into the range 0
	 *            to 1
	 *
	 * @returns integer color to display
	 *
	 */
	protected final int indexIntoGradientsArrays(float position) {

		// first, manipulate position value depending on the cycle method.

		if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {

			if (position >= 1) { // upper bound is 1
				return gradientOverflow;
			}

			else if (position <= 0) { // lower bound is 0
				return gradientUnderflow;
			}
		}

		else if (cycleMethod == MultipleGradientPaint.REPEAT) {
			// get the fractional part
			// (modulo behavior discards integer component)
			position = position - (int) position;

			// position now be between -1 and 1

			if (position < 0) {
				position = position + 1; // force it to be in the range 0-1
			}

			int w = 0, c1 = 0, c2 = 0;
			if (isSimpleLookup) {
				position *= gradient.length;
				int idx1 = (int) (position);
				if (idx1 + 1 < gradient.length) {
					return gradient[idx1];
				}

				w = (int) ((position - idx1) * (1 << 16));
				c1 = gradient[idx1];
				c2 = gradient[0];
			} else {
				// for all the gradient interval arrays
				for (int i = 0; i < gradientsLength; i++) {

					if (position < fractions[i + 1]) { // this is the array we
														// want

						float delta = position - fractions[i];

						delta = ((delta / normalizedIntervals[i])
								* GRADIENT_SIZE);
						// this is the interval we want.
						int index = (int) delta;
						if ((index + 1 < gradients[i].length)
								|| (i + 1 < gradientsLength)) {
							return gradients[i][index];
						}

						w = (int) ((delta - index) * (1 << 16));
						c1 = gradients[i][index];
						c2 = gradients[0][0];
						break;
					}
				}
			}

			return ((((((c1 >> 8) & 0xFF0000)
					+ ((((c2 >>> 24)) - ((c1 >>> 24))) * w)) & 0xFF0000) << 8) |

					(((((c1) & 0xFF0000)
							+ ((((c2 >> 16) & 0xFF) - ((c1 >> 16) & 0xFF)) * w))
							& 0xFF0000))
					|

					(((((c1 << 8) & 0xFF0000)
							+ ((((c2 >> 8) & 0xFF) - ((c1 >> 8) & 0xFF)) * w))
							& 0xFF0000) >> 8)
					|

					(((((c1 << 16) & 0xFF0000)
							+ ((((c2) & 0xFF) - ((c1) & 0xFF)) * w))
							& 0xFF0000) >> 16));

			// return c1 +
			// ((( ((((c2>>>24) )-((c1>>>24) ))*w)&0xFF0000)<< 8) |
			// (( ((((c2>> 16)&0xFF)-((c1>> 16)&0xFF))*w)&0xFF0000) ) |
			// (( ((((c2>> 8)&0xFF)-((c1>> 8)&0xFF))*w)&0xFF0000)>> 8) |
			// (( ((((c2 )&0xFF)-((c1 )&0xFF))*w)&0xFF0000)>>16));
		}

		else { // cycleMethod == MultipleGradientPaint.REFLECT

			if (position < 0) {
				position = -position; // take absolute value
			}

			int part = (int) position; // take the integer part

			position = position - part; // get the fractional part

			if ((part & 0x00000001) == 1) { // if integer part is odd
				position = 1 - position; // want the reflected color instead
			}
		}

		// now, get the color based on this 0-1 position:

		if (isSimpleLookup) { // easy to compute: just scale index by array size
			return gradient[(int) (position * fastGradientArraySize)];
		}
		// for all the gradient interval arrays
		for (int i = 0; i < gradientsLength; i++) {

			if (position < fractions[i + 1]) { // this is the array we want

				float delta = position - fractions[i];

				// this is the interval we want.
				int index = (int) ((delta / normalizedIntervals[i])
						* (GRADIENT_SIZE_INDEX));

				return gradients[i][index];
			}
		}

		return gradientOverflow;
	}

	/**
	 * Helper function to index into the gradients array. This is necessary
	 * because each interval has an array of colors with uniform size 255.
	 * However, the color intervals are not necessarily of uniform length, so a
	 * conversion is required. This version also does anti-aliasing by averaging
	 * the gradient over position+/-(sz/2).
	 *
	 * @param position
	 *            the unmanipulated position. want to map this into the range 0
	 *            to 1
	 * @param sz
	 *            the size in gradient space to average.
	 *
	 * @returns ARGB integer color to display
	 */
	protected final int indexGradientAntiAlias(float position, float sz) {
		// first, manipulate position value depending on the cycle method.
		if (cycleMethod == MultipleGradientPaint.NO_CYCLE) {
			if (DEBUG) {
				System.out.println("NO_CYCLE");
			}
			float p1 = position - (sz / 2);
			float p2 = position + (sz / 2);

			if (p1 >= 1) {
				return gradientOverflow;
			}

			if (p2 <= 0) {
				return gradientUnderflow;
			}

			int interior;
			float top_weight = 0, bottom_weight = 0, frac;
			if (p2 >= 1) {
				top_weight = (p2 - 1) / sz;
				if (p1 <= 0) {
					bottom_weight = -p1 / sz;
					frac = 1;
					interior = gradientAverage;
				} else {
					frac = 1 - p1;
					interior = getAntiAlias(p1, true, 1, false, 1 - p1, 1);
				}
			} else if (p1 <= 0) {
				bottom_weight = -p1 / sz;
				frac = p2;
				interior = getAntiAlias(0, true, p2, false, p2, 1);
			} else {
				return getAntiAlias(p1, true, p2, false, sz, 1);
			}

			int norm = (int) ((1 << 16) * frac / sz);
			int pA = (((interior >>> 20) & 0xFF0) * norm) >> 16;
			int pR = (((interior >> 12) & 0xFF0) * norm) >> 16;
			int pG = (((interior >> 4) & 0xFF0) * norm) >> 16;
			int pB = (((interior << 4) & 0xFF0) * norm) >> 16;

			if (bottom_weight != 0) {
				int bPix = gradientUnderflow;
				// System.out.println("ave: " + gradientAverage);
				norm = (int) ((1 << 16) * bottom_weight);
				pA += (((bPix >>> 20) & 0xFF0) * norm) >> 16;
				pR += (((bPix >> 12) & 0xFF0) * norm) >> 16;
				pG += (((bPix >> 4) & 0xFF0) * norm) >> 16;
				pB += (((bPix << 4) & 0xFF0) * norm) >> 16;
			}

			if (top_weight != 0) {
				int tPix = gradientOverflow;

				norm = (int) ((1 << 16) * top_weight);
				pA += (((tPix >>> 20) & 0xFF0) * norm) >> 16;
				pR += (((tPix >> 12) & 0xFF0) * norm) >> 16;
				pG += (((tPix >> 4) & 0xFF0) * norm) >> 16;
				pB += (((tPix << 4) & 0xFF0) * norm) >> 16;
			}

			return (((pA & 0xFF0) << 20) | ((pR & 0xFF0) << 12)
					| ((pG & 0xFF0) << 4) | ((pB & 0xFF0) >> 4));
		}

		// See how many times we are going to "wrap around" the gradient,
		// array.
		int intSz = (int) sz;

		float weight = 1f;
		if (intSz != 0) {
			// We need to make sure that sz is < 1.0 otherwise
			// p1 and p2 my pass each other which will cause no end of
			// trouble.
			sz -= intSz;
			weight = sz / (intSz + sz);
			if (weight < 0.1) {
				// The part of the color from the location will be swamped
				// by the averaged part of the gradient so just use the
				// average color for the gradient.
				return gradientAverage;
			}
		}

		// So close to full gradient just use the average value...
		if (sz > 0.99) {
			return gradientAverage;
		}

		// Go up and down from position by 1/2 sz.
		float p1 = position - (sz / 2);
		float p2 = position + (sz / 2);
		if (DEBUG) {
			System.out.println("P1: " + p1 + " P2: " + p2);
		}

		// These indicate the direction to go from p1 and p2 when
		// averaging...
		boolean p1_up = true;
		boolean p2_up = false;

		if (cycleMethod == MultipleGradientPaint.REPEAT) {
			if (DEBUG) {
				System.out.println("REPEAT");
			}

			// Get positions between -1 and 1
			p1 = p1 - (int) p1;
			p2 = p2 - (int) p2;

			// force to be in rage 0-1.
			if (p1 < 0) {
				p1 += 1;
			}
			if (p2 < 0) {
				p2 += 1;
			}
		}

		else { // cycleMethod == MultipleGradientPaint.REFLECT
			if (DEBUG) {
				System.out.println("REFLECT");
			}

			// take absolute values
			// Note when we reflect we change sense of p1/2_up.
			if (p2 < 0) {
				p1 = -p1;
				p1_up = !p1_up;
				p2 = -p2;
				p2_up = !p2_up;
			} else if (p1 < 0) {
				p1 = -p1;
				p1_up = !p1_up;
			}

			int part1, part2;
			part1 = (int) p1; // take the integer part
			p1 = p1 - part1; // get the fractional part

			part2 = (int) p2; // take the integer part
			p2 = p2 - part2; // get the fractional part

			// if integer part is odd we want the reflected color instead.
			// Note when we reflect we change sense of p1/2_up.
			if ((part1 & 0x01) == 1) {
				p1 = 1 - p1;
				p1_up = !p1_up;
			}

			if ((part2 & 0x01) == 1) {
				p2 = 1 - p2;
				p2_up = !p2_up;
			}

			// Check if in the end they just got switched around.
			// this commonly happens if they both end up negative.
			if ((p1 > p2) && !p1_up && p2_up) {
				float t = p1;
				p1 = p2;
				p2 = t;
				p1_up = true;
				p2_up = false;
			}
		}

		return getAntiAlias(p1, p1_up, p2, p2_up, sz, weight);
	}

	private final int getAntiAlias(float p1, boolean p1_up, float p2,
			boolean p2_up, float sz, float weight) {

		// Until the last set of ops these are 28.4 fixed point values.
		int ach = 0, rch = 0, gch = 0, bch = 0;
		if (isSimpleLookup) {
			p1 *= fastGradientArraySize;
			p2 *= fastGradientArraySize;

			int idx1 = (int) p1;
			int idx2 = (int) p2;

			int i, pix;

			if (p1_up && !p2_up && (idx1 <= idx2)) {

				if (idx1 == idx2) {
					return gradient[idx1];
				}

				// Sum between idx1 and idx2.
				for (i = idx1 + 1; i < idx2; i++) {
					pix = gradient[i];
					ach += ((pix >>> 20) & 0xFF0);
					rch += ((pix >>> 12) & 0xFF0);
					gch += ((pix >>> 4) & 0xFF0);
					bch += ((pix << 4) & 0xFF0);
				}
			} else {
				// Do the bulk of the work, all the whole gradient entries
				// for idx1 and idx2.
				if (p1_up) {
					for (i = idx1 + 1; i < fastGradientArraySize; i++) {
						pix = gradient[i];
						ach += ((pix >>> 20) & 0xFF0);
						rch += ((pix >>> 12) & 0xFF0);
						gch += ((pix >>> 4) & 0xFF0);
						bch += ((pix << 4) & 0xFF0);
					}
				} else {
					for (i = 0; i < idx1; i++) {
						pix = gradient[i];
						ach += ((pix >>> 20) & 0xFF0);
						rch += ((pix >>> 12) & 0xFF0);
						gch += ((pix >>> 4) & 0xFF0);
						bch += ((pix << 4) & 0xFF0);
					}
				}

				if (p2_up) {
					for (i = idx2 + 1; i < fastGradientArraySize; i++) {
						pix = gradient[i];
						ach += ((pix >>> 20) & 0xFF0);
						rch += ((pix >>> 12) & 0xFF0);
						gch += ((pix >>> 4) & 0xFF0);
						bch += ((pix << 4) & 0xFF0);
					}
				} else {
					for (i = 0; i < idx2; i++) {
						pix = gradient[i];
						ach += ((pix >>> 20) & 0xFF0);
						rch += ((pix >>> 12) & 0xFF0);
						gch += ((pix >>> 4) & 0xFF0);
						bch += ((pix << 4) & 0xFF0);
					}
				}
			}

			int norm, isz;

			// Normalize the summation so far...
			isz = (int) ((1 << 16) / (sz * fastGradientArraySize));
			ach = (ach * isz) >> 16;
			rch = (rch * isz) >> 16;
			gch = (gch * isz) >> 16;
			bch = (bch * isz) >> 16;

			// Clean up with the partial buckets at each end.
			if (p1_up) {
				norm = (int) ((1 - (p1 - idx1)) * isz);
			} else {
				norm = (int) ((p1 - idx1) * isz);
			}
			pix = gradient[idx1];
			ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
			rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
			gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
			bch += (((pix << 4) & 0xFF0) * norm) >> 16;

			if (p2_up) {
				norm = (int) ((1 - (p2 - idx2)) * isz);
			} else {
				norm = (int) ((p2 - idx2) * isz);
			}
			pix = gradient[idx2];
			ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
			rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
			gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
			bch += (((pix << 4) & 0xFF0) * norm) >> 16;

			// Round and drop the 4bits frac.
			ach = (ach + 0x08) >> 4;
			rch = (rch + 0x08) >> 4;
			gch = (gch + 0x08) >> 4;
			bch = (bch + 0x08) >> 4;

		} else {
			int idx1 = 0, idx2 = 0;
			int i1 = -1, i2 = -1;
			float f1 = 0, f2 = 0;
			// Find which gradient interval our points fall into.
			for (int i = 0; i < gradientsLength; i++) {
				if ((p1 < fractions[i + 1]) && (i1 == -1)) {
					// this is the array we want
					i1 = i;
					f1 = p1 - fractions[i];

					f1 = ((f1 / normalizedIntervals[i]) * GRADIENT_SIZE_INDEX);
					// this is the interval we want.
					idx1 = (int) f1;
					if (i2 != -1) {
						break;
					}
				}
				if ((p2 < fractions[i + 1]) && (i2 == -1)) {
					// this is the array we want
					i2 = i;
					f2 = p2 - fractions[i];

					f2 = ((f2 / normalizedIntervals[i]) * GRADIENT_SIZE_INDEX);
					// this is the interval we want.
					idx2 = (int) f2;
					if (i1 != -1) {
						break;
					}
				}
			}

			if (i1 == -1) {
				i1 = gradients.length - 1;
				f1 = idx1 = GRADIENT_SIZE_INDEX;
			}

			if (i2 == -1) {
				i2 = gradients.length - 1;
				f2 = idx2 = GRADIENT_SIZE_INDEX;
			}

			if (DEBUG) {
				System.out.println("I1: " + i1 + " Idx1: " + idx1 + " I2: " + i2
						+ " Idx2: " + idx2);
			}

			// Simple case within one gradient array (so the average
			// of the two idx gives us the true average of colors).
			if ((i1 == i2) && (idx1 <= idx2) && p1_up && !p2_up) {
				return gradients[i1][(idx1 + idx2 + 1) >> 1];
			}

			// i1 != i2

			int pix, norm;
			int base = (int) ((1 << 16) / sz);
			if ((i1 < i2) && p1_up && !p2_up) {
				norm = (int) ((base * normalizedIntervals[i1]
						* (GRADIENT_SIZE_INDEX - f1)) / GRADIENT_SIZE_INDEX);
				pix = gradients[i1][(idx1 + GRADIENT_SIZE) >> 1];
				ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
				rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
				gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
				bch += (((pix << 4) & 0xFF0) * norm) >> 16;

				for (int i = i1 + 1; i < i2; i++) {
					norm = (int) (base * normalizedIntervals[i]);
					pix = gradients[i][GRADIENT_SIZE >> 1];

					ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
					rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
					gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
					bch += (((pix << 4) & 0xFF0) * norm) >> 16;
				}

				norm = (int) ((base * normalizedIntervals[i2] * f2)
						/ GRADIENT_SIZE_INDEX);
				pix = gradients[i2][(idx2 + 1) >> 1];
				ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
				rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
				gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
				bch += (((pix << 4) & 0xFF0) * norm) >> 16;
			} else {
				if (p1_up) {
					norm = (int) ((base * normalizedIntervals[i1]
							* (GRADIENT_SIZE_INDEX - f1))
							/ GRADIENT_SIZE_INDEX);
					pix = gradients[i1][(idx1 + GRADIENT_SIZE) >> 1];
				} else {
					norm = (int) ((base * normalizedIntervals[i1] * f1)
							/ GRADIENT_SIZE_INDEX);
					pix = gradients[i1][(idx1 + 1) >> 1];
				}
				ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
				rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
				gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
				bch += (((pix << 4) & 0xFF0) * norm) >> 16;

				if (p2_up) {
					norm = (int) ((base * normalizedIntervals[i2]
							* (GRADIENT_SIZE_INDEX - f2))
							/ GRADIENT_SIZE_INDEX);
					pix = gradients[i2][(idx2 + GRADIENT_SIZE) >> 1];
				} else {
					norm = (int) ((base * normalizedIntervals[i2] * f2)
							/ GRADIENT_SIZE_INDEX);
					pix = gradients[i2][(idx2 + 1) >> 1];
				}
				ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
				rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
				gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
				bch += (((pix << 4) & 0xFF0) * norm) >> 16;

				if (p1_up) {
					for (int i = i1 + 1; i < gradientsLength; i++) {
						norm = (int) (base * normalizedIntervals[i]);
						pix = gradients[i][GRADIENT_SIZE >> 1];

						ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
						rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
						gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
						bch += (((pix << 4) & 0xFF0) * norm) >> 16;
					}
				} else {
					for (int i = 0; i < i1; i++) {
						norm = (int) (base * normalizedIntervals[i]);
						pix = gradients[i][GRADIENT_SIZE >> 1];

						ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
						rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
						gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
						bch += (((pix << 4) & 0xFF0) * norm) >> 16;
					}
				}

				if (p2_up) {
					for (int i = i2 + 1; i < gradientsLength; i++) {
						norm = (int) (base * normalizedIntervals[i]);
						pix = gradients[i][GRADIENT_SIZE >> 1];

						ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
						rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
						gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
						bch += (((pix << 4) & 0xFF0) * norm) >> 16;
					}
				} else {
					for (int i = 0; i < i2; i++) {
						norm = (int) (base * normalizedIntervals[i]);
						pix = gradients[i][GRADIENT_SIZE >> 1];

						ach += (((pix >>> 20) & 0xFF0) * norm) >> 16;
						rch += (((pix >>> 12) & 0xFF0) * norm) >> 16;
						gch += (((pix >>> 4) & 0xFF0) * norm) >> 16;
						bch += (((pix << 4) & 0xFF0) * norm) >> 16;
					}
				}

			}
			ach = (ach + 0x08) >> 4;
			rch = (rch + 0x08) >> 4;
			gch = (gch + 0x08) >> 4;
			bch = (bch + 0x08) >> 4;
			if (DEBUG) {
				System.out.println("Pix: [" + ach + ", " + rch + ", " + gch
						+ ", " + bch + "]");
			}
		}

		if (weight != 1) {
			// System.out.println("ave: " + gradientAverage);
			int aveW = (int) ((1 << 16) * (1 - weight));
			int aveA = ((gradientAverage >>> 24) & 0xFF) * aveW;
			int aveR = ((gradientAverage >> 16) & 0xFF) * aveW;
			int aveG = ((gradientAverage >> 8) & 0xFF) * aveW;
			int aveB = ((gradientAverage) & 0xFF) * aveW;

			int iw = (int) (weight * (1 << 16));
			ach = ((ach * iw) + aveA) >> 16;
			rch = ((rch * iw) + aveR) >> 16;
			gch = ((gch * iw) + aveG) >> 16;
			bch = ((bch * iw) + aveB) >> 16;
		}

		return ((ach << 24) | (rch << 16) | (gch << 8) | bch);
	}

	/**
	 * Helper function to convert a color component in sRGB space to linear RGB
	 * space. Used to build a static lookup table.
	 */
	private static int convertSRGBtoLinearRGB(int color) {

		float input, output;

		input = (color) / 255.0f;
		if (input <= 0.04045f) {
			output = input / 12.92f;
		} else {
			output = (float) Math.pow((input + 0.055) / 1.055, 2.4);
		}
		int o = Math.round(output * 255.0f);

		return o;
	}

	/**
	 * Helper function to convert a color component in linear RGB space to SRGB
	 * space. Used to build a static lookup table.
	 */
	private static int convertLinearRGBtoSRGB(int color) {

		float input, output;

		input = (color) / 255.0f;

		if (input <= 0.0031308) {
			output = input * 12.92f;
		} else {
			output = (1.055f * ((float) Math.pow(input, (1.0 / 2.4)))) - 0.055f;
		}

		int o = Math.round(output * 255.0f);

		return o;
	}

	/** Superclass getRaster... */
	@Override
	public final Raster getRaster(int x, int y, int w, int h) {
		if (w == 0 || h == 0) {
			return null;
		}

		//
		// If working raster is big enough, reuse it. Otherwise,
		// build a large enough new one.
		//
		WritableRaster raster = saved;
		if (raster == null || raster.getWidth() < w || raster.getHeight() < h) {
			raster = getCachedRaster(dataModel, w, h);
			saved = raster;
		}

		// Access raster internal int array. Because we use a DirectColorModel,
		// we know the DataBuffer is of type DataBufferInt and the SampleModel
		// is SinglePixelPackedSampleModel.
		// Adjust for initial offset in DataBuffer and also for the scanline
		// stride.
		//
		DataBufferInt rasterDB = (DataBufferInt) raster.getDataBuffer();
		int[] pixels = rasterDB.getBankData()[0];
		int off = rasterDB.getOffset();
		int scanlineStride = ((SinglePixelPackedSampleModel) raster
				.getSampleModel()).getScanlineStride();
		int adjust = scanlineStride - w;

		fillRaster(pixels, off, adjust, x, y, w, h); // delegate to subclass.

		GraphicsUtil.coerceData(raster, dataModel,
				model.isAlphaPremultiplied());

		return raster;
	}

	/** Subclasses should implement this. */
	protected abstract void fillRaster(int pixels[], int off, int adjust, int x,
			int y, int w, int h);

	/**
	 * Took this cacheRaster code from GradientPaint. It appears to recycle
	 * rasters for use by any other instance, as long as they are sufficiently
	 * large.
	 */
	protected final static synchronized WritableRaster getCachedRaster(
			ColorModel cm, int w, int h) {
		if (cm == cachedModel) {
			if (cached != null) {
				WritableRaster ras = (WritableRaster) cached.get();
				if (ras != null && ras.getWidth() >= w
						&& ras.getHeight() >= h) {
					cached = null;
					return ras;
				}
			}
		}
		// Don't create rediculously small rasters...
		if (w < 32) {
			w = 32;
		}
		if (h < 32) {
			h = 32;
		}
		return cm.createCompatibleWritableRaster(w, h);
	}

	/**
	 * Took this cacheRaster code from GradientPaint. It appears to recycle
	 * rasters for use by any other instance, as long as they are sufficiently
	 * large.
	 */
	protected final static synchronized void putCachedRaster(ColorModel cm,
			WritableRaster ras) {
		if (cached != null) {
			WritableRaster cras = (WritableRaster) cached.get();
			if (cras != null) {
				int cw = cras.getWidth();
				int ch = cras.getHeight();
				int iw = ras.getWidth();
				int ih = ras.getHeight();
				if (cw >= iw && ch >= ih) {
					return;
				}
				if (cw * ch >= iw * ih) {
					return;
				}
			}
		}
		cachedModel = cm;
		cached = new WeakReference(ras);
	}

	/**
	 * Release the resources allocated for the operation.
	 */
	@Override
	public final void dispose() {
		if (saved != null) {
			putCachedRaster(model, saved);
			saved = null;
		}
	}

	/**
	 * Return the ColorModel of the output.
	 */
	@Override
	public final ColorModel getColorModel() {
		return model;
	}
}
